﻿#region License
// /* **************************************************************
// * Copyright (C) intex Consulting GmbH - All Rights Reserved    *
// *--------------------------------------------------------------*
// * Unauthorized copying of this file, via any medium is         *
// * strictly prohibited                                          *
// *--------------------------------------------------------------*
// * Proprietary and confidential                                 *
// *--------------------------------------------------------------*
// * Dissasembling/modifications strictly prohibited!             *
// ***************************************************************/
#endregion
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using ComHlp.enums;
using ComHlp.Exceptions;

namespace ComHlp.Classes.Net
{
    public class CodepageConverter
    {
        private readonly ItxCodepage _codePage;

        public bool IsAs400 => _codePage == ItxCodepage.Cp273;


        public CodepageConverter(ItxCodepage codePage)
        {
            _codePage = codePage;
        }

        /// <exception cref="EncodingException">Condition.</exception>
        public string GetString(byte[] bytes, int offSet, int count)
        {
            switch (_codePage)
            {
                case ItxCodepage.Cp273:
                    return Cp273To850(bytes, offSet, count);
                default:
                    return ConvertWinToUnicode(bytes, offSet, count);
            }
        }

        /// <exception cref="EncodingException">Condition.</exception>
        /// <exception cref="FormatException"><paramref>
        ///         <name>value</name>
        ///     </paramref>
        ///     does not consist of an optional sign followed by a sequence of digits (0 through 9). </exception>
        /// <exception cref="ArgumentNullException"><paramref>
        ///         <name>s</name>
        ///     </paramref>
        ///     is null. </exception>
        /// <exception cref="OverflowException"><paramref>
        ///         <name>value</name>
        ///     </paramref>
        ///     represents a number that is less than <see cref="F:System.UInt32.MinValue" /> or greater than <see cref="F:System.UInt32.MaxValue" />. </exception>
        public byte[] GetBytes(string data)
        {
            switch (_codePage)
            {
                case ItxCodepage.Cp273:
                    return Cp850To273(data);
                default:
                    return ConvertUnicodeToWin(data);
            }
        }

        /// <summary> Convert received bytes from corrosponding codepage used on windows to unicode </summary>
        /// <param name="bytes"></param>
        /// <param name="offSet"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        /// <exception cref="EncodingException">Condition.</exception>
        private string ConvertWinToUnicode(byte[] bytes, int offSet, int count)
        {
            if(bytes == null)
                throw new EncodingException("No data received!");

            try
            {
                return ComStatics.Encoder.GetString(bytes, offSet, count);
            }
            catch (DecoderFallbackException exc)
            {
                throw new EncodingException(exc, "Error encoding: {0}", exc.Message);
            }
        }

        /// <summary> Convert passed string to bytes in corrosponding codepage used on windows </summary>
        /// <param name="sendString"></param>
        /// <returns></returns>
        /// <exception cref="EncodingException">Condition.</exception>
        private byte[] ConvertUnicodeToWin(string sendString)
        {
            try
            {
                return ComStatics.Encoder.GetBytes(sendString);
            }
            catch (EncoderFallbackException exc)
            {
                throw new EncodingException(exc, "Error encoding: {0}", exc.Message);
            }
        }


        /// <summary> Convert received bytes from codepage 273 (ebcdic) to codepage 850 </summary>
        /// <param name="bytes"></param>
        /// <param name="index"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        /// <exception cref="EncodingException">Can't find symbol: {0}</exception>
        private string Cp273To850(byte[] bytes, int index, int count)
        {
            // init
            StringBuilder sb = new StringBuilder();

            // parse the quested area of the bytearray
            for (int i = index; i < count; i++)
            {
                byte b = bytes[i];

                // got message end
                if (b == 0x00)
                    break;

                string hexStr = $"{b:x2}";

                int cpIndex = _cp273.IndexOf(hexStr);
                if (cpIndex <= -1)
                    throw new EncodingException("Can't find symbol: {0}", hexStr);

                int value;
                try
                {
                    value = Convert.ToInt32(_cp850[cpIndex], 16);
                }
                catch (OverflowException exc)
                {
                    throw new EncodingException(exc, "Can't find symbol: {0}", hexStr);
                }
                sb.Append((char) value);
            }

            return sb.ToString();
        }

        /// <summary> Convert string from codepage 850 to byte array codepage 273 (ebcdic) </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="EncodingException">Can't find symbol: {0}</exception>
        /// <exception cref="FormatException"><paramref>
        ///         <name>value</name>
        ///     </paramref>
        ///     does not consist of an optional sign followed by a sequence of digits (0 through 9). </exception>
        /// <exception cref="ArgumentNullException"><paramref>
        ///         <name>s</name>
        ///     </paramref>
        ///     is null. </exception>
        /// <exception cref="OverflowException"><paramref>
        ///         <name>value</name>
        ///     </paramref>
        ///     represents a number that is less than <see cref="F:System.UInt32.MinValue" /> or greater than <see cref="F:System.UInt32.MaxValue" />. </exception>
        private byte[] Cp850To273(string input)
        {
            int pos = 0;
            byte[] retValue = new byte[input.Length];

            foreach (char c in input)
            {
                int iTmp = c;
                string hexStr = $"{Convert.ToUInt32(iTmp.ToString(CultureInfo.InvariantCulture)):x2}";

                int index = _cp850.IndexOf(hexStr);
                if (index <= -1)
                    throw new EncodingException("Can't find symbol: {0}", hexStr);

                retValue[pos] = byte.Parse(_cp273[index], NumberStyles.HexNumber);
                pos++;
            }
            return retValue;
        }


        /// <summary> Internal ebcdic translation table </summary>
        private readonly List<string> _cp273 = new List<string>(new[]
            {
                "00", "01", "02", "03", "37", "2d", "2e", "2f", "16", "05", "25", "0b", "0c", "0d", "0e", "0f", "10", "11", "12", "13", "3c", "3d", "32", "26", "18", "19", "3f", "27", "1c", "1d", "1e", "1f", "40", "4f", "7f", "7b", "5b", "6c", "50", "7d", "4d", "5d", "5c", "4e", "6b", "60", "4b", "61", "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "7a", "5e", "4c", "7e", "6e", "6f", "b5", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "63", "ec", "fc", "5f", "6d", "79", "81", "82", "83", "84", "85", "86", "87", "88", "89", "91", "92", "93", "94", "95", "96", "97", "98", "99", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "43", "bb", "dc", "59", "07", "20", "21", "22", "23", "24", "15", "06", "17", "28", "29", "2a", "2b", "2c", "09", "0a", "1b", "30", "31", "1a", "33", "34", "35", "36", "08", "38", "39", "3a", "3b", "04", "14", "3e", "ff", "41", "aa", "b0", "b1", "9f", "b2", "cc", "7c", "bd", "b4", "9a", "8a", "ba", "ca", "af", "bc", "90", "8f", "ea", "fa", "be", "a0", "b6", "b3", "9d", "da", "9b", "8b", "b7", "b8", "b9", "ab", "64", "65", "62", "66", "4a", "67", "9e", "68", "74", "71", "72", "73", "78", "75", "76", "77", "ac", "69", "ed", "ee", "eb", "ef", "e0", "bf", "80", "fd", "fe", "fb", "5a", "ad", "ae", "a1", "44", "45", "42", "46", "c0", "47", "9c", "48", "54", "51", "52", "53", "58", "55", "56", "57", "8c", "49", "cd", "ce", "cb", "cf", "6a", "e1", "70", "dd", "de", "db", "d0", "8d", "8e", "df"
            });

        /// <summary> Internal cp850 translation table </summary>
        private readonly List<string> _cp850 = new List<string>(new[]
            {
                "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5a", "5b", "5c", "5d", "5e", "5f", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", "6e", "6f", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d", "7e", "7f", "c4", "b3", "c0", "d9", "bf", "da", "c3", "c1", "b4", "c2", "c5", "b0", "b1", "b2", "d5", "f2", "cd", "ba", "c8", "bc", "bb", "c9", "cc", "ca", "b9", "cb", "ce", "df", "dc", "db", "fe", "9f", "ff", "ad", "bd", "9c", "cf", "be", "dd", "f5", "f9", "b8", "a6", "ae", "aa", "f0", "a9", "ee", "f8", "f1", "fd", "fc", "ef", "e6", "f4", "fa", "f7", "fb", "a7", "af", "ac", "ab", "f3", "a8", "b7", "b5", "b6", "c7", "8e", "8f", "92", "80", "d4", "90", "d2", "d3", "de", "d6", "d7", "d8", "d1", "a5", "e3", "e0", "e2", "e5", "99", "9e", "9d", "eb", "e9", "ea", "9a", "ed", "e8", "e1", "85", "a0", "83", "c6", "84", "86", "91", "87", "8a", "82", "88", "89", "8d", "a1", "8c", "8b", "d0", "a4", "95", "a2", "93", "e4", "94", "f6", "9b", "97", "a3", "96", "81", "ec", "e7", "98"
            });
    }
}
